package erfgame.core.world.terrain.file;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.io.FileUtils;

import erfgame.core.world.World;
import erfgame.core.world.WorldDAO;

public class FileWorldDAO implements WorldDAO {

	private static final String WORLD_FILE = "world.ser";
	private static final Logger LOGGER = Logger.getLogger( FileWorldDAO.class.getName() );
	
	private File baseDirectory;
	
	public FileWorldDAO( File baseDirectory ) {
		this.baseDirectory = baseDirectory;
	}

	public World getWorld(int worldId) {
		File worldFile = getFile( worldId );
		if( worldFile.exists() ) {
			return readWorld( worldFile );
		} else {
			return null;
		}
	}

	public List<World> getWorlds() {
		File[] files = this.baseDirectory.listFiles( new FileFilter() {

			public boolean accept(File pathname) {
				boolean result;
				if( pathname.isDirectory() ) {
					try {
						Integer.parseInt( pathname.getName() );
						result = true;
					} catch( NumberFormatException ex ) {
						result = false;
					}
				} else {
					result = false;
				}
				return result;
			}
		});
		
		ArrayList<World> result;
		if( files != null ) {
			result= new ArrayList<World>( files.length );
	
			for( int i=0; i<files.length; i++ ) {
				World world = readWorld( new File( files[ i ], WORLD_FILE ) );
				if( world != null ) {
					result.add( world );
				}
			}
		} else {
			result = new ArrayList<World>(0);
		}
		return result;
	}
	
	private World readWorld( File file ) {
		FileInputStream fis = null;
		try {
			fis = new FileInputStream( file );
			ObjectInputStream oins = new ObjectInputStream( fis );
			return (World)oins.readObject();
		} catch( Exception ex ) {
			LOGGER.log( Level.WARNING, "unable to read world file "+file.getPath(), ex );
			return null;
		} finally {
			if( fis != null ) {
				try {
					fis.close();
				} catch( IOException ex ) {
					LOGGER.log( Level.WARNING, "unable to close world file "+file.getPath(), ex );
				}
			}
		}
	}
	
	public File getFile( int worldId ) {
		File directory = new File( this.baseDirectory, Integer.toString( worldId ) );
		if( !directory.exists() ) {
			directory.mkdirs();
		}
		return new File( directory, WORLD_FILE );
	}

	public void storeWorld(World world) {
		File file = getFile( world.getWorldId() );
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream( file );
			ObjectOutputStream oouts = new ObjectOutputStream( fos );
			oouts.writeObject( world );
			oouts.flush();
		} catch( Exception ex ) {
			throw new RuntimeException( "unable to write "+file.getPath(), ex );
		} finally {
			if( fos != null ) {
				try {
					fos.close();
				} catch( IOException ex ) {
					LOGGER.log( Level.WARNING, "unable to close world file "+file.getPath() );
				}
			}
		}
	}

	public void deleteWorld(int worldId) {
		File directory = new File( this.baseDirectory, Integer.toString( worldId ) );
		if( directory.exists() ) {
			try {
				FileUtils.deleteDirectory( directory );
			} catch( IOException ex ) {
				throw new RuntimeException( "unable to delete "+directory, ex );
			}
		}
	}
}
